/******************************************************************************
glsl.h
Version: 1.2.0
Last update: 2010/10/06 

(c) 2003-2010 by Martin Christen. All Rights reserved.
******************************************************************************/

#include "glsl.h"

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <cassert>
#include <GL/glew.h>

using namespace std;

//-----------------------------------------------------------------------------
/*
Initialization:
   glShaderManager SM;
   glShader *shader = SM.loadfromFile("test.vert","test.frag");
   if (shader==0) 
      cout << "Error Loading, compiling or linking shader\n";

   index_myFloat = shader->GetUniformLocation("myFloat");

Render:
   shader->begin();
   shader->setUniform1f(index_myFloat, 1.123);
     glutDrawSolidSphere(1.0);
   shader->end();


Geometry Shader
The easiest way to use Geometry Shaders is through the
Shadermanager. 
Initialization:
 SM.SetInputPrimitiveType(GL_TRIANGLES);
 SM.SetOutputPrimitiveType(GL_TRIANGLE_STRIP);
 SM.SetVerticesOut(3);
 glShader *shader = SM.loadfromFile("test.vert","test.geom","test.frag");
*/

//-----------------------------------------------------------------------------
// Error, Warning and Info Strings
char* aGLSLStrings[] = {
        "[e00] GLSL is not available!",
        "[e01] Not a valid program object!",
        "[e02] Not a valid object!",
        "[e03] Out of memory!",
        "[e04] Unknown compiler error!",
        "[e05] Linker log is not available!",
        "[e06] Compiler log is not available!",
        "[Empty]"
        };
//-----------------------------------------------------------------------------      
 
// GL ERROR CHECK
int CheckGLError(char *file, int line)
{
   GLenum glErr;
   int    retCode = 0;

   glErr = glGetError();
   while (glErr != GL_NO_ERROR) 
    {
     const GLubyte* sError = gluErrorString(glErr);

	 if (sError)
		cout << "GL Error #" << glErr << "(" << gluErrorString(glErr) << ") " << " in File " << file << " at line: " << line << endl;
	 else
	    cout << "GL Error #" << glErr << " (no message available)" << " in File " << file << " at line: " << line << endl;
		
	 retCode = 1;
	 glErr = glGetError();
   }
   
#ifdef _DEBUG
   if (retCode)
      assert(false);
#endif

   return retCode;
}
   
#define CHECK_GL_ERROR() CheckGLError(__FILE__, __LINE__)

//----------------------------------------------------------------------------- 

// ************************************************************************
// Implementation of glShader class
// ************************************************************************
 
glShader::glShader()
{
  ProgramObject = 0;
  linker_log = 0;
  is_linked = false; 
  _mM = false;

  ProgramObject = glCreateProgram();
}

//----------------------------------------------------------------------------- 

glShader::~glShader()
{
   if (linker_log!=0) free(linker_log);

   for (unsigned int i=0;i<ShaderList.size();i++)
   {
        glDetachShader(ProgramObject, ShaderList[i]->ShaderObject);
        CHECK_GL_ERROR(); // if you get an error here, you deleted the Program object first and then
                       // the ShaderObject! Always delete ShaderObjects last!
        if (_mM) delete ShaderList[i]; 
   }                      
}

//----------------------------------------------------------------------------- 

void glShader::addShader(glShaderObject* ShaderProgram)
{
   if (ShaderProgram==0) return;

   if (!ShaderProgram->is_compiled)
   {
        cout << "**warning** please compile program before adding object! trying to compile now...\n";
        if (!ShaderProgram->compile())
        {
            cout << "...compile ERROR!\n";
            return;
        }
        else
        {   
            cout << "...ok!\n";
        }
   }

   ShaderList.push_back(ShaderProgram); 
   
}

//----------------------------------------------------------------------------- 

void glShader::SetInputPrimitiveType(int nInputPrimitiveType)
{
   _nInputPrimitiveType = nInputPrimitiveType;
}

void glShader::SetOutputPrimitiveType(int nOutputPrimitiveType)
{
   _nOutputPrimitiveType = nOutputPrimitiveType;
}

void glShader::SetVerticesOut(int nVerticesOut)
{
   _nVerticesOut = nVerticesOut;
}
//----------------------------------------------------------------------------- 

bool glShader::link(void)
{

unsigned int i;

    if (_bUsesGeometryShader)
	{
		glProgramParameteriEXT(ProgramObject, GL_GEOMETRY_INPUT_TYPE_EXT, _nInputPrimitiveType);
		glProgramParameteriEXT(ProgramObject, GL_GEOMETRY_OUTPUT_TYPE_EXT, _nOutputPrimitiveType);
		glProgramParameteriEXT(ProgramObject, GL_GEOMETRY_VERTICES_OUT_EXT, _nVerticesOut);
    }

    if (is_linked)  // already linked, detach everything first
    {
       cout << "**warning** Object is already linked, trying to link again" << endl;
       for (i=0;i<ShaderList.size();i++)
       {
            glDetachShader(ProgramObject, ShaderList[i]->ShaderObject);
            CHECK_GL_ERROR();
       }
    }
    
    for (i=0;i<ShaderList.size();i++)
    {
        glAttachShader(ProgramObject, ShaderList[i]->ShaderObject);
        CHECK_GL_ERROR();
        //cout << "attaching ProgramObj [" << i << "] @ 0x" << hex << ShaderList[i]->ProgramObject << " in ShaderObj @ 0x"  << ShaderObject << endl;
    }
    
    GLint linked; // bugfix Oct-06-2006
    glLinkProgram(ProgramObject);
    CHECK_GL_ERROR();
    glGetProgramiv(ProgramObject, GL_LINK_STATUS, &linked);
    CHECK_GL_ERROR();

    if (linked)
    {
        is_linked = true;
        return true;
    }
    else
    {
        cout << "**linker error**\n";
    }

return false;
}

//----------------------------------------------------------------------------- 
// Compiler Log: linker message to string

char* glShader::getLinkerLog(void)
{    
 GLint blen = 0;   
 GLsizei slen = 0;

 if (ProgramObject==0) return aGLSLStrings[2];
 glGetProgramiv(ProgramObject, GL_INFO_LOG_LENGTH , &blen);
 CHECK_GL_ERROR();

 if (blen > 1)
 {
    if (linker_log!=0) 
    {   
        free(linker_log);
        linker_log =0;
    }
    if ((linker_log = (GLchar*)malloc(blen)) == NULL) 
     {
        printf("ERROR: Could not allocate compiler_log buffer\n");
        return aGLSLStrings[3];
    }

    glGetProgramInfoLog(ProgramObject, blen, &slen, linker_log);
    CHECK_GL_ERROR();
    
 }
 if (linker_log!=0)
    return (char*) linker_log;    
 else
    return aGLSLStrings[5];
   
 return aGLSLStrings[4];
}

void glShader::Bind(void)
{
if (ProgramObject == 0) return;

    if (is_linked)
    {
        glUseProgram(ProgramObject);
        CHECK_GL_ERROR();
    }
}

//----------------------------------------------------------------------------- 

void glShader::Unbind(void)
{
    glUseProgram(0);
    CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform1f(GLint loc, GLfloat v0)
{   
    glUniform1f(loc, v0); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform2f(GLint loc, GLfloat v0, GLfloat v1)
{
    glUniform2f(loc, v0, v1); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform3f(GLint loc, GLfloat v0, GLfloat v1, GLfloat v2)
{
    glUniform3f(loc, v0, v1, v2); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform4f(GLint loc, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
{
    glUniform4f(loc, v0, v1, v2, v3); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform1i(GLint loc, GLint v0)
{ 
    glUniform1i(loc, v0); CHECK_GL_ERROR();
}

//-----------------------------------------------------------------------------

void glShader::setUniform2i(GLint loc, GLint v0, GLint v1)
{
    glUniform2i(loc, v0, v1); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform3i(GLint loc, GLint v0, GLint v1, GLint v2)
{
    glUniform3i(loc, v0, v1, v2); CHECK_GL_ERROR();
}

//-----------------------------------------------------------------------------

void glShader::setUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3)
{    
    glUniform4i(loc, v0, v1, v2, v3); CHECK_GL_ERROR();
}
//-----------------------------------------------------------------------------
//----------------------------------------------------------------------------- 

void glShader::setUniform1ui(GLint loc, GLuint v0)
{  
    glUniform1ui(loc, v0); CHECK_GL_ERROR();
}

//-----------------------------------------------------------------------------

void glShader::setUniform2ui(GLint loc, GLuint v0, GLuint v1)
{
    if (!GLEW_EXT_gpu_shader4) return;
    
    
    glUniform2ui(loc, v0, v1); CHECK_GL_ERROR();

}

//----------------------------------------------------------------------------- 

void glShader::setUniform3ui(GLint loc, GLuint v0, GLuint v1, GLuint v2)
{
    if (!GLEW_EXT_gpu_shader4) return;
    
    glUniform3ui(loc, v0, v1, v2); CHECK_GL_ERROR();
}

//-----------------------------------------------------------------------------

void glShader::setUniform4ui(GLint loc, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
{
    if (!GLEW_EXT_gpu_shader4) return;
    
    glUniform4ui(loc, v0, v1, v2, v3); CHECK_GL_ERROR();
}
//-----------------------------------------------------------------------------

void glShader::setUniform1fv(GLint loc, GLsizei count, GLfloat *value)
{
    glUniform1fv(loc, count, value); CHECK_GL_ERROR();
}
//----------------------------------------------------------------------------- 

void glShader::setUniform2fv(GLint loc, GLsizei count, GLfloat *value)
{   
    glUniform2fv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform3fv(GLint loc, GLsizei count, GLfloat *value)
{
    glUniform3fv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform4fv(GLint loc, GLsizei count, GLfloat *value)
{
    glUniform4fv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform1iv(GLint loc, GLsizei count, GLint *value)
{  
    glUniform1iv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform2iv(GLint loc, GLsizei count, GLint *value)
{
    glUniform2iv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform3iv(GLint loc, GLsizei count, GLint *value)
{
    glUniform3iv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform4iv(GLint loc, GLsizei count, GLint *value)
{ 
    glUniform4iv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform1uiv(GLint loc, GLsizei count, GLuint *value)
{
    if (!GLEW_EXT_gpu_shader4) return;
    glUniform1uiv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform2uiv(GLint loc, GLsizei count, GLuint *value)
{
    if (!GLEW_EXT_gpu_shader4) return;
   
    glUniform2uiv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform3uiv(GLint loc, GLsizei count, GLuint *value)
{
    if (!GLEW_EXT_gpu_shader4) return;

    glUniform3uiv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniform4uiv(GLint loc, GLsizei count, GLuint *value)
{
    if (!GLEW_EXT_gpu_shader4) return;

    glUniform4uiv(loc, count, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniformMatrix2fv(GLint loc, GLsizei count, GLboolean transpose, GLfloat *value)
{
    glUniformMatrix2fv(loc, count, transpose, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniformMatrix3fv(GLint loc, GLsizei count, GLboolean transpose, GLfloat *value)
{
    glUniformMatrix3fv(loc, count, transpose, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::setUniformMatrix4fv(GLint loc, GLsizei count, GLboolean transpose, GLfloat *value)
{
    glUniformMatrix4fv(loc, count, transpose, value); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

GLint glShader::GetUniformLocation(const GLchar *name)
{
	GLint loc;

	loc = glGetUniformLocation(ProgramObject, name);
	if (loc == -1) 
	{
        cout << "Error: can't find uniform variable \"" << name << "\"\n";
	}
   
   CHECK_GL_ERROR();
	
   return loc;
}

//----------------------------------------------------------------------------- 

void glShader::getUniformfv(GLint loc, GLfloat* values)
{
   glGetUniformfv(ProgramObject, loc, values); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::getUniformiv(GLint loc, GLint* values)
{
	glGetUniformiv(ProgramObject, loc, values); CHECK_GL_ERROR();
}

//----------------------------------------------------------------------------- 

void glShader::getUniformuiv(GLint loc, GLuint* values)
{
	glGetUniformuiv(ProgramObject, loc, values); CHECK_GL_ERROR();
}

//-----------------------------------------------------------------------------
void  glShader::BindAttribLocation(GLint index, GLchar* name)
{
   glBindAttribLocation(ProgramObject, index, name);
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib1f(GLuint index, GLfloat v0)
{
   glVertexAttrib1f(index, v0);

   return true;
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib2f(GLuint index, GLfloat v0, GLfloat v1)
{
   glVertexAttrib2f(index, v0, v1);
   
   return true;
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib3f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2)
{
    glVertexAttrib3f(index, v0, v1, v2);
    
    return true;
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib4f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
{
   glVertexAttrib4f(index, v0, v1, v2, v3);
   
   return true;
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib1d(GLuint index, GLdouble v0)
{
   glVertexAttrib1d(index, v0);

   return true;
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib2d(GLuint index, GLdouble v0, GLdouble v1)
{
   glVertexAttrib2d(index, v0, v1);

   return true;
}

//-----------------------------------------------------------------------------
bool glShader::setVertexAttrib3d(GLuint index, GLdouble v0, GLdouble v1, GLdouble v2)
{
   glVertexAttrib3d(index, v0, v1, v2);

   return true;
}
//-----------------------------------------------------------------------------
bool glShader::setVertexAttrib4d(GLuint index, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3)
{
   glVertexAttrib4d(index, v0, v1, v2, v3);

   return true;
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib1s(GLuint index, GLshort v0)
{
   glVertexAttrib1s(index, v0);

   return true;
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib2s(GLuint index, GLshort v0, GLshort v1)
{
   glVertexAttrib2s(index, v0, v1);

   return true;
}

//-----------------------------------------------------------------------------
bool glShader::setVertexAttrib3s(GLuint index, GLshort v0, GLshort v1, GLshort v2)
{
   glVertexAttrib3s(index, v0, v1, v2);

   return true;
}
//-----------------------------------------------------------------------------
bool glShader::setVertexAttrib4s(GLuint index, GLshort v0, GLshort v1, GLshort v2, GLshort v3)
{
   glVertexAttrib4s(index, v0, v1, v2, v3);

   return true;
}
//----------------------------------------------------------------------------
bool glShader::setVertexAttribNormalizedByte(GLuint index, GLbyte v0, GLbyte v1, GLbyte v2, GLbyte v3)
{
   glVertexAttrib4Nub(index, v0, v1, v2, v3);

   return true;
}
//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib1i(GLuint index, GLint v0)
{
   if (!GLEW_EXT_gpu_shader4) return false;

   glVertexAttribI1i(index, v0);

   return true;
}

//-----------------------------------------------------------------------------

bool glShader::setVertexAttrib2i(GLuint index, GLint v0, GLint v1)
{
   if (!GLEW_EXT_gpu_shader4) return false;

   glVertexAttribI2i(index, v0, v1);

   return true;
}

//-----------------------------------------------------------------------------
bool glShader::setVertexAttrib3i(GLuint index, GLint v0, GLint v1, GLint v2)
{
   if (!GLEW_EXT_gpu_shader4) return false;

   glVertexAttribI3i(index, v0, v1, v2);

   return true;
}
//-----------------------------------------------------------------------------
bool glShader::setVertexAttrib4i(GLuint index, GLint v0, GLint v1, GLint v2, GLint v3)
{
   if (!GLEW_EXT_gpu_shader4) return false;

   glVertexAttribI4i(index, v0, v1, v2, v3);

   return true;
}
//-----------------------------------------------------------------------------
bool glShader::setVertexAttrib1ui(GLuint index, GLuint v0)
{
   if (!GLEW_EXT_gpu_shader4) return false;

   glVertexAttribI1ui(index, v0);

   return true;
}

//------------------------------------------------------------------------------

bool glShader::setVertexAttrib2ui(GLuint index, GLuint v0, GLuint v1)
{
   if (!GLEW_EXT_gpu_shader4) return false;

   glVertexAttribI2ui(index, v0, v1);

   return true;
}

//------------------------------------------------------------------------------
bool glShader::setVertexAttrib3ui(GLuint index, GLuint v0, GLuint v1, GLuint v2)
{
   if (!GLEW_EXT_gpu_shader4) return false;

   glVertexAttribI3ui(index, v0, v1, v2);

   return true;
}
//------------------------------------------------------------------------------
bool glShader::setVertexAttrib4ui(GLuint index, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
{
   if (!GLEW_EXT_gpu_shader4) return false;

   glVertexAttribI4ui(index, v0, v1, v2, v3);

   return true;
}
//------------------------------------------------------------------------------
// ************************************************************************
// Shader Program : Manage Shader Programs (Vertex/Fragment)
// ************************************************************************
glShaderObject::glShaderObject()
{
    compiler_log = 0;
    is_compiled = false;
    program_type = 0;
    ShaderObject = 0;
    ShaderSource = 0;
    _memalloc = false;
}

//------------------------------------------------------------------------------
glShaderObject::~glShaderObject()
{
   if (compiler_log!=0) free(compiler_log);
   if (ShaderSource!=0)   
   {
        if (_memalloc)
            delete[] ShaderSource;  // free ASCII Source
   }
   
   if (is_compiled)
   { 
        glDeleteObjectARB(ShaderObject);
        CHECK_GL_ERROR();
   }
}

//------------------------------------------------------------------------------
unsigned long getFileLength(ifstream& file)
{
    if(!file.good()) return 0;
    
    unsigned long pos=file.tellg();
    file.seekg(0,ios::end);
    unsigned long len = file.tellg();
    file.seekg(pos);
    
    return len;
}

//------------------------------------------------------------------------------ 
int glShaderObject::load(const char* filename)
{
   ifstream file;
	file.open(filename, ios::in);
   if(!file) return -1;
    
   unsigned long len = getFileLength(file);
    
   if (len==0) return -2;   // "Empty File" 
    
   if (ShaderSource!=0)    // there is already a source loaded, free it!
   {
      if (_memalloc)
      delete[] ShaderSource;
   }
    
   ShaderSource = (GLubyte*) new char[len+1];
   if (ShaderSource == 0) return -3;   // can't reserve memory
   _memalloc = true;
     
   ShaderSource[len] = 0;  // len isn't always strlen cause some characters are stripped in ascii read...
                           // it is important to 0-terminate the real length later, len is just max possible value...
   unsigned int i=0;
   while (file.good())
   {
       ShaderSource[i] = file.get();       // get character from file.
       if (!file.eof())
        i++;
   }
    
   ShaderSource[i] = 0;  // 0 terminate it.
    
   file.close();
      
   return 0;
}

//------------------------------------------------------------------------------
void glShaderObject::loadFromMemory(const char* program)
{
    if (ShaderSource!=0)    // there is already a source loaded, free it!
    {
        if (_memalloc)
        delete[] ShaderSource;
    }
   _memalloc = false;
   ShaderSource = (GLubyte*) program;
      
}

//------------------------------------------------------------------------------
// Compiler Log: return messages from compiler 
char* glShaderObject::getCompilerLog(void)
{    
 GLint blen = 0;	
 GLsizei slen = 0;	


 if (ShaderObject==0) return aGLSLStrings[1]; // not a valid program object

 glGetShaderiv(ShaderObject, GL_INFO_LOG_LENGTH , &blen);
 CHECK_GL_ERROR();

 if (blen > 1)
 {
    if (compiler_log!=0) 
    {   
        free(compiler_log);
        compiler_log =0;
    }
    if ((compiler_log = (GLchar*)malloc(blen)) == NULL) 
     {
        printf("ERROR: Could not allocate compiler_log buffer\n");
        return aGLSLStrings[3];
    }

     glGetInfoLogARB(ShaderObject, blen, &slen, compiler_log);
     CHECK_GL_ERROR();
     //cout << "compiler_log: \n", compiler_log);     
 }
 if (compiler_log!=0)
    return (char*) compiler_log;
 else
   return aGLSLStrings[6];       

 return aGLSLStrings[4];
}

//------------------------------------------------------------------------------

bool glShaderObject::compile(void)
{
is_compiled = false;

GLint compiled = 0;

  if (ShaderSource==0) return false;

  GLint	length = (GLint) strlen((const char*)ShaderSource);
  glShaderSource(ShaderObject, 1, (const GLchar **)&ShaderSource, &length);
  CHECK_GL_ERROR();

  glCompileShader(ShaderObject); 
  CHECK_GL_ERROR();
  glGetObjectParameterivARB(ShaderObject, GL_COMPILE_STATUS, &compiled);
  CHECK_GL_ERROR();

  if (compiled) is_compiled=true;
 
return is_compiled;
}

//------------------------------------------------------------------------------
GLint glShaderObject::getAttribLocation(char* attribName)
{
   return glGetAttribLocation(ShaderObject, attribName);
}

//------------------------------------------------------------------------------
VertexShader::VertexShader()
{
  program_type = 1; 
  ShaderObject = glCreateShader(GL_VERTEX_SHADER);
  CHECK_GL_ERROR();
}

//------------------------------------------------------------------------------

VertexShader::~VertexShader()
{
}
//------------------------------------------------------------------------------

FragmentShader::FragmentShader()
{
    program_type = 2;
    ShaderObject = glCreateShader(GL_FRAGMENT_SHADER); 
    CHECK_GL_ERROR();
}

//------------------------------------------------------------------------------

FragmentShader::~FragmentShader()
{
}


//------------------------------------------------------------------------------

GeometryShader::GeometryShader()
{
  program_type = 3; 
  ShaderObject = glCreateShader(GL_GEOMETRY_SHADER);
  CHECK_GL_ERROR();
}

//------------------------------------------------------------------------------

GeometryShader::~GeometryShader()
{
}

//------------------------------------------------------------------------------
// ShaderManager: Easy use of (multiple) Shaders

glShaderManager::glShaderManager()
{
   _nInputPrimitiveType = GL_TRIANGLES;
   _nOutputPrimitiveType = GL_TRIANGLE_STRIP;
   _nVerticesOut = 3;
   
}

glShaderManager::~glShaderManager()
{
   // free objects
   vector<glShader*>::iterator  i=_shaderObjectList.begin();
   while (i!=_shaderObjectList.end()) 
   {
        //glShader* o = *i;
        i=_shaderObjectList.erase(i);
        //delete o;
   }
}

//------------------------------------------------------------------------------

void glShaderManager::SetInputPrimitiveType(int nInputPrimitiveType)
{
      _nInputPrimitiveType = nInputPrimitiveType;

}

void glShaderManager::SetOutputPrimitiveType(int nOutputPrimitiveType)
{
      _nOutputPrimitiveType = nOutputPrimitiveType;

}

void glShaderManager::SetVerticesOut(int nVerticesOut)
{
   _nVerticesOut = nVerticesOut;
}

//------------------------------------------------------------------------------

glShader* glShaderManager::loadfromFile(const char* vertexFile, const char* fragmentFile, bool bLink) 
{
   glShader* o = new glShader();
   o->UsesGeometryShader(false);
  
   VertexShader* tVertexShader = new VertexShader;
   FragmentShader* tFragmentShader = new FragmentShader;

    // load vertex program
   if (vertexFile!=0)
   if (tVertexShader->load(vertexFile) != 0)
   { 
     cout << "error: can't load vertex shader!\n"; 
     delete o;
     delete tVertexShader;
     delete tFragmentShader;
     return 0;
   }

  
  // Load fragment program
  if (fragmentFile!=0)
  if (tFragmentShader->load(fragmentFile) != 0)
  {
     cout << "error: can't load fragment shader!\n";
     delete o;
     delete tVertexShader;
     delete tFragmentShader;
     return 0;
  }
    
  // Compile vertex program
  if (vertexFile!=0)
  if (!tVertexShader->compile())
  {
      cout << "***COMPILER ERROR (Vertex Shader):\n";
      cout << tVertexShader->getCompilerLog() << endl;
      delete o;
      delete tVertexShader;
      delete tFragmentShader;
      return 0;
  }
  cout << "***GLSL Compiler Log (Vertex Shader):\n";
  cout << tVertexShader->getCompilerLog() << "\n";
   
  // Compile fragment program 
  if (fragmentFile!=0)
  if (!tFragmentShader->compile())
  {
     cout << "***COMPILER ERROR (Fragment Shader):\n";
     cout << tFragmentShader->getCompilerLog() << endl;
     
     delete o;
     delete tVertexShader;
     delete tFragmentShader;
     return 0;
     
  }
  cout << "***GLSL Compiler Log (Fragment Shader):\n";
  cout << tFragmentShader->getCompilerLog() << "\n";
   
  // Add to object    
  if (vertexFile!=0) o->addShader(tVertexShader);
  if (fragmentFile!=0) o->addShader(tFragmentShader); 
  
  // link 
  if (bLink)
  {
     if (!o->link())
     {
        cout << "**LINKER ERROR\n";
        cout << o->getLinkerLog() << endl;
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        return 0;
     }
     cout << "***GLSL Linker Log:\n";
     cout << o->getLinkerLog() << endl;
  }
  
  _shaderObjectList.push_back(o);
  o->manageMemory();

   return o;
}

//------------------------------------------------------------------------------

glShader* glShaderManager::loadfromFile(const char* vertexFile, const char* geometryFile, const char* fragmentFile, bool bLink)
{
   glShader* o = new glShader();
   o->UsesGeometryShader(true);
   o->SetInputPrimitiveType(_nInputPrimitiveType);
   o->SetOutputPrimitiveType(_nOutputPrimitiveType);
   o->SetVerticesOut(_nVerticesOut);
  
   VertexShader* tVertexShader = new VertexShader;
   FragmentShader* tFragmentShader = new FragmentShader;
   GeometryShader* tGeometryShader = new GeometryShader;

    // load vertex program
   if (vertexFile!=0)
      if (tVertexShader->load(vertexFile) != 0)
      { 
        cout << "error: can't load vertex shader!\n"; 
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
      }

  // Load geometry program
  if (geometryFile!=0)
     if (tGeometryShader->load(geometryFile) != 0)
     {
        cout << "error: can't load geometry shader!\n";
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
     }
  
  // Load fragment program
  if (fragmentFile!=0)
     if (tFragmentShader->load(fragmentFile) != 0)
     {
        cout << "error: can't load fragment shader!\n";
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
     }
    
  // Compile vertex program
  if (vertexFile!=0)
     if (!tVertexShader->compile())
     {
         cout << "***COMPILER ERROR (Vertex Shader):\n";
         cout << tVertexShader->getCompilerLog() << endl;
         delete o;
         delete tVertexShader;
         delete tFragmentShader;
         delete tGeometryShader;
         return 0;
     }
  cout << "***GLSL Compiler Log (Vertex Shader):\n";
  cout << tVertexShader->getCompilerLog() << "\n";

  // Compile geometry program 
  if (geometryFile!=0)
  {
     
     if (!tGeometryShader->compile())
     {
        cout << "***COMPILER ERROR (Geometry Shader):\n";
        cout << tGeometryShader->getCompilerLog() << endl;
        
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
        
     }
  }
  cout << "***GLSL Compiler Log (Geometry Shader):\n";
  cout << tGeometryShader->getCompilerLog() << "\n";
   
  // Compile fragment program 
  if (fragmentFile!=0)
     if (!tFragmentShader->compile())
     {
        cout << "***COMPILER ERROR (Fragment Shader):\n";
        cout << tFragmentShader->getCompilerLog() << endl;
        
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
        
     }
  cout << "***GLSL Compiler Log (Fragment Shader):\n";
  cout << tFragmentShader->getCompilerLog() << "\n";
   
  // Add to object    
  if (vertexFile!=0) o->addShader(tVertexShader);
  if (geometryFile!=0) o->addShader(tGeometryShader);
  if (fragmentFile!=0) o->addShader(tFragmentShader); 
  
  // link 
  if (bLink)
  {
     if (!o->link())
     {
        cout << "**LINKER ERROR\n";
        cout << o->getLinkerLog() << endl;
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
     }
     cout << "***GLSL Linker Log:\n";
     cout << o->getLinkerLog() << endl;
  }
  
  _shaderObjectList.push_back(o);
  o->manageMemory();

  return o;
}
//------------------------------------------------------------------------------

glShader* glShaderManager::loadfromMemory(const char* vertexMem, const char* fragmentMem, bool bLink)
{
  glShader* o = new glShader();
  o->UsesGeometryShader(false);
  
  VertexShader* tVertexShader = new VertexShader;
  FragmentShader* tFragmentShader = new FragmentShader;

  // get vertex program
  if (vertexMem!=0)
     tVertexShader->loadFromMemory(vertexMem);
  
  // get fragment program
  if (fragmentMem!=0)
     tFragmentShader->loadFromMemory(fragmentMem);
 
  // Compile vertex program
  if (vertexMem!=0)
  if (!tVertexShader->compile())
  {
      cout << "***COMPILER ERROR (Vertex Shader):\n";
      cout << tVertexShader->getCompilerLog() << endl;
      delete o;
      delete tVertexShader;
      delete tFragmentShader;
      return 0;
  }
  cout << "***GLSL Compiler Log (Vertex Shader):\n";
  cout << tVertexShader->getCompilerLog() << "\n";
   
  // Compile fragment program 
  if (fragmentMem!=0)
  if (!tFragmentShader->compile())
  {
     cout << "***COMPILER ERROR (Fragment Shader):\n";
     cout << tFragmentShader->getCompilerLog() << endl;
     
     delete o;
     delete tVertexShader;
     delete tFragmentShader;
     return 0;   
  }
  cout << "***GLSL Compiler Log (Fragment Shader):\n";
  cout << tFragmentShader->getCompilerLog() << "\n";
   
  // Add to object    
  if (vertexMem!=0) o->addShader(tVertexShader);
  if (fragmentMem!=0) o->addShader(tFragmentShader); 
  
  // link 
  if (bLink)
  {
     if (!o->link())
     {
        cout << "**LINKER ERROR\n";
        cout << o->getLinkerLog() << endl;
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        return 0;
     }
     cout << "***GLSL Linker Log:\n";
     cout << o->getLinkerLog() << endl;
  }

  _shaderObjectList.push_back(o);
   o->manageMemory();

  return o;
}

//-----------------------------------------------------------------------------

glShader* glShaderManager::loadfromMemory(const char* vertexMem, const char* geometryMem, const char* fragmentMem, bool bLink)
{
  glShader* o = new glShader();
  o->UsesGeometryShader(true);
  o->SetInputPrimitiveType(_nInputPrimitiveType);
  o->SetOutputPrimitiveType(_nOutputPrimitiveType);
  o->SetVerticesOut(_nVerticesOut);
  
  VertexShader* tVertexShader = new VertexShader;
  FragmentShader* tFragmentShader = new FragmentShader;
  GeometryShader* tGeometryShader = new GeometryShader;

  // get vertex program
  if (vertexMem!=0)
     tVertexShader->loadFromMemory(vertexMem);
  
  // get fragment program
  if (fragmentMem!=0)
     tFragmentShader->loadFromMemory(fragmentMem);

    // get fragment program
  if (geometryMem!=0)
     tGeometryShader->loadFromMemory(geometryMem);
 
  // Compile vertex program
  if (vertexMem!=0)
  if (!tVertexShader->compile())
  {
      cout << "***COMPILER ERROR (Vertex Shader):\n";
      cout << tVertexShader->getCompilerLog() << endl;
      delete o;
      delete tVertexShader;
      delete tFragmentShader;
      delete tGeometryShader;
      return 0;
  }
  cout << "***GLSL Compiler Log (Vertex Shader):\n";
  cout << tVertexShader->getCompilerLog() << "\n";

  // Compile geometry program
  if (geometryMem!=0)
  if (!tGeometryShader->compile())
  {
      cout << "***COMPILER ERROR (Geometry Shader):\n";
      cout << tGeometryShader->getCompilerLog() << endl;
      delete o;
      delete tVertexShader;
      delete tFragmentShader;
      delete tGeometryShader;
      return 0;
  }
  cout << "***GLSL Compiler Log (Geometry Shader):\n";
  cout << tVertexShader->getCompilerLog() << "\n";
   
  // Compile fragment program 
  if (fragmentMem!=0)
  if (!tFragmentShader->compile())
  {
     cout << "***COMPILER ERROR (Fragment Shader):\n";
     cout << tFragmentShader->getCompilerLog() << endl;
     
     delete o;
     delete tVertexShader;
     delete tFragmentShader;
     delete tGeometryShader;
     return 0;   
  }
  cout << "***GLSL Compiler Log (Fragment Shader):\n";
  cout << tFragmentShader->getCompilerLog() << "\n";
   
  // Add to object    
  if (vertexMem!=0) o->addShader(tVertexShader);
  if (geometryMem!=0) o->addShader(tGeometryShader);
  if (fragmentMem!=0) o->addShader(tFragmentShader); 

  // link 
  if (bLink)
  {
     if (!o->link())
     {
        cout << "**LINKER ERROR\n";
        cout << o->getLinkerLog() << endl;
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
     }
     cout << "***GLSL Linker Log:\n";
     cout << o->getLinkerLog() << endl;
  }

  _shaderObjectList.push_back(o);
  o->manageMemory();

   return o;
}

// ----------------------------------------------------------------------------

 bool  glShaderManager::freeshader(glShader* o)
 {
   vector<glShader*>::iterator  i=_shaderObjectList.begin();
   while (i!=_shaderObjectList.end()) 
   {
        if ((*i)==o)
        {
            _shaderObjectList.erase(i);
            delete o;
            return true;
        }
        i++;
   }   
   return false;
 }

//-----------------------------------------------------------------------------



